package org.freespace.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.freespace.config.StringUtil;
import org.freespace.mapper.JobDetailMapper;
import org.freespace.model.*;
import org.freespace.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.freespace.utils.InfluxDBUtils;
import org.freespace.utils.enums.JobStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author liuqi
 * @since 2023-06-14
 */
@Slf4j
@Service("jobDetailService")
public class JobDetailServiceImpl extends ServiceImpl<JobDetailMapper, JobDetail> implements IJobDetailService {
    @Autowired
    IJobService jobService;

    @Autowired
    InfluxDBUtils influxDBUtils;

    @Autowired
    IJobMachineService jobMachineService;

    @Autowired
    ITestplanService testplanService;

    @Autowired
    IJmxSampleService jmxSampleService;
    @Override
    public List<Map<String, String>> apiPerfList(Integer testplanId, Integer jobId) {
        Job job = jobService.getById(jobId);
        Integer status = job.getStatus();

        LocalDateTime startTime = job.getStartTime();
        LocalDateTime endTime = Optional.ofNullable(job.getEndTime()).orElse(LocalDateTime.now());

        List<Map<String, String>> perfIndexsList = new ArrayList<>();

        List<Map<String, Object>> agreReport = getAggregateReport(testplanId, jobId);

        for (Map<String, Object> report : agreReport) {
            log.info("jobId:{} report:{}", jobId, report);
            Map<String, String> perfIndexs = new HashMap<>();

            String samples = "";
            String errors = "";
            String line90 = "";
            String line95 = "";
            String line99 = "";
            String errorPercent = "";

            if (CollectionUtil.isNotEmpty(report)) {
                Double count = Double.parseDouble(report.get("samples") + "");
                samples = count.longValue() + "";
                Double errorCount = Double.parseDouble(report.get("error") + "");
                if (errorCount != null) {
                    errors = errorCount.longValue() + "";
                }

                line90 = report.get("line90") + "";
                line95 = report.get("line95") + "";
                line99 = report.get("line99") + "";
                errorPercent = new BigDecimal(report.get("errorPercent").toString()).doubleValue() + "";
                if (report.get("avg") == null || "".equals(report.get("avg"))) {
                    perfIndexs.put("avg", "");
                } else {
                    perfIndexs.put("avg", report.get("avg") + "");
                }

                perfIndexs.put("duration", Duration.between(startTime, endTime).getSeconds() + "");
                perfIndexs.put("starttime", startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                perfIndexs.put("endtime", endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                perfIndexs.put("samples", samples);
                perfIndexs.put("error", errors);
                perfIndexs.put("avg", report.get("avg")+"");
                perfIndexs.put("errorPercent", errorPercent);
                perfIndexs.put("maxTps", report.get("maxTps") + "");
                perfIndexs.put("line90", line90);
                perfIndexs.put("line95", line95);
                perfIndexs.put("line99", line99);
                perfIndexs.put("num_of_thread", samples);
                perfIndexs.put("label", report.get("label") + "");
                perfIndexs.put("min", report.get("min") + "");
                perfIndexs.put("max", report.get("max") + "");
                perfIndexs.put("tps", report.get("tps") + "");
                perfIndexsList.add(perfIndexs);
            }
        }
        return perfIndexsList;
    }

    @Override
    public List<Map<String, Object>> getAggregateReport(Integer testplanId, Integer jobId) {
        Job job = jobService.getById(jobId);
        List<Map<String, Object>> result = new ArrayList<>();
        if (job.getStatus().equals(JobStatus.FINISHED.getType())) {
            // 已完成的job,直接查询mysql
           
            List<JobDetail> detailList = this.list(Wrappers.<JobDetail>lambdaQuery().eq(JobDetail::getJobId, jobId).eq(JobDetail::getTestplanId, testplanId));
            Map<String, Map> totals = new HashMap<>();
            for (JobDetail detail : detailList) {
                log.info("jobId:{} detail:{}", jobId, detail);
                Map<String, Object> map = new HashMap<>();
                map.put("label",detail.getLabel());
                map.put("samples",detail.getSamples());
                map.put("error",detail.getError());
                map.put("avg",detail.getAvg());
                map.put("line90",detail.getLine90());
                map.put("line95",detail.getLine95());
                map.put("line99",detail.getLine99());
                map.put("min",detail.getMin());
                map.put("max",detail.getMax());
                map.put("errPercent",detail.getErrorPercent());
                map.put("tps",detail.getTps());
                map.put("maxTps",detail.getMaxTps());
                map.put("duration",detail.getDuration());

                if (detail.getLabel().equals("Total")) {
                    totals.put("Total", map);
                } else {
                    result.add(map);
                }
            }

            if (CollectionUtil.isNotEmpty(totals)) {
                result.add(totals.get("Total"));
            }

            // 兼容处理
            if (result.isEmpty()) {
                result = influxDBUtils.getInfluxDbData(jobId);
            }
            
            
        } else {
            //未完成状态，查询influxdb
            result = influxDBUtils.getInfluxDbData(jobId);
        }

        Testplan testplan = testplanService.getById(job.getTestplanId());
        for (Map<String, Object> map : result) {
            if (!"Total".equals(map.get("label"))) {
                LambdaQueryWrapper<JmxSample> w = Wrappers.<JmxSample>lambdaQuery().eq(JmxSample::getJmxId, testplan.getJmxId()).eq(JmxSample::getName, map.get("label"));
                List<JmxSample> jmxSamples = jmxSampleService.list(w);
                if (CollectionUtil.isNotEmpty(jmxSamples)) {
                    String path = jmxSamples.get(0).getPath();
                    String url = StringUtils.isNotBlank(path) ? path : "/";
                    map.put("url", url);
                } else {
                    map.put("url", "/");
                }
            }

        }

        return result;
    }

    @Override
    public JobDetail getDynamicInfo(Integer testplanId, Integer jobId) {

        Job job = jobService.getById(jobId);

        // threadnum = machinenum * influx_max_threadnum
        Integer thread = 0;
        Integer threadCount = influxDBUtils.queryThreadNum(testplanId, jobId);

        if (threadCount != null) {
            int machineNum = jobMachineService.getMachineNum(jobId);
            thread = machineNum * threadCount;
        }

        // 已完成，直接返回total
        if (job != null && job.getStatus() == JobStatus.FINISHED.getType()) {
             JobDetail totalDetail = getOne(Wrappers.<JobDetail>lambdaQuery().eq(JobDetail::getJobId, job.getId()).eq(JobDetail::getLabel, "Total"));
            if (totalDetail != null) {
                totalDetail.setThreadNum(thread);
                return totalDetail;
            }
        }

        JobDetail totalDetail = new JobDetail();

//
//        String[] ipArr = job.getIps().split(",", -1);
//        List<String> ips = Arrays.asList(ipArr);
//

        List<Map<String, Object>> influxDbData = influxDBUtils.getInfluxDbData(jobId);
        for (Map<String, Object> influxDbDatum : influxDbData) {
            if (influxDbDatum.get("label").toString().equals("Total")) {
                totalDetail.setTestplanId(testplanId);
                totalDetail.setJobId(jobId);
                totalDetail.setThreadNum((Integer) influxDbDatum.get("num_of_thread"));
                totalDetail.setDuration((String) influxDbDatum.get("duration"));
                totalDetail.setTps((String) influxDbDatum.get("tps"));
                totalDetail.setMaxTps((String) influxDbDatum.get("maxTps"));
                totalDetail.setAvg((String) influxDbDatum.get("avg"));
                totalDetail.setLine90((String) influxDbDatum.get("line90"));
                totalDetail.setLine95((String) influxDbDatum.get("line95"));
                totalDetail.setLine99((String) influxDbDatum.get("line99"));
                totalDetail.setSamples((String) influxDbDatum.get("samples"));
                totalDetail.setError((String) influxDbDatum.get("error"));
                totalDetail.setErrorPercent((String) influxDbDatum.get("errorPercent"));
                totalDetail.setLabel((String) influxDbDatum.get("label"));
//                totalDetail.setUrl("");
                totalDetail.setMin((String) influxDbDatum.get("min"));
                totalDetail.setMax((String) influxDbDatum.get("max"));
//                totalDetail.setMedian((String) influxDbDatum.get("median"));
//                totalDetail.setReceived((String) influxDbDatum.get("received"));
//                totalDetail.setSent((String) influxDbDatum.get("sent"));
            }
        }
        return totalDetail;
    }
}
